#include "Arduino.h"
#include "max7219.h"
#include "pixelbuffer.h"
#include "settings.h"




void max7219_byte(uint8_t data) {
  // Sends one byte to the max7219 chip
  for(uint8_t i = 8; i != 0; i--) {
    digitalWrite(MAX7219_CLK, LOW);  // Set CLK to LOW
    if (data & 0b10000000)    // Mask the MSB of the data
      digitalWrite(MAX7219_DIN, HIGH);  // Set DIN to HIGH
    else
      digitalWrite(MAX7219_DIN, LOW);  // Set DIN to LOW
    digitalWrite(MAX7219_CLK, HIGH);  // Set CLK to HIGH
    data <<= 1;  // Shift to the left
  }
}


void max7219_word(uint8_t addr, uint8_t data, uint8_t num) {
  // Sends one full command to the max7219 chip in the corresponding position

  digitalWrite(MAX7219_CS, LOW);  // Set CS to LOW ____ start of transmission
  for (uint8_t n = num; n != 0; n--) {
    // Send multiple times for cascaded matrices
    max7219_byte(addr);  // Sending the address
    max7219_byte(data);  // Sending the data
  }
  digitalWrite(MAX7219_CS, HIGH);  // Set CS to HIGH \__ end of transmission
  digitalWrite(MAX7219_CLK, LOW);  // Set CLK to LOW /
}


void Max7219::init(void) {
  pinMode(MAX7219_CS, OUTPUT);
  pinMode(MAX7219_DIN, OUTPUT);
  pinMode(MAX7219_CLK, OUTPUT);

  const uint8_t max7219_initseq[] = {
    0x09, 0x00, // Decode-Mode Register, 00 = No decode
    0x0a, MAX7219_INTENSITY_LEVEL,  // Intensity Register, 0x00 .. 0x0f
    0x0b, 0x07, // Scan-Limit Register, 0x07 to show all lines
    0x0c, 0x01, // Shutdown Register, 0x01 = Normal Operation
    0x0f, 0x00, // Display-Test Register, 0x01, 0x00 = Normal
  };
  
  for (uint8_t i = 0; i < sizeof (max7219_initseq);) {
    uint8_t addr = max7219_initseq[i++];
    uint8_t data = max7219_initseq[i++];
    max7219_word(addr, data, MAX7219_SEG_NUM);
  }
}


void Max7219::set_illumination(int illumination_level) {
  illumination_level = constrain(illumination_level, 0x00, 0x0a);

  for(uint8_t i=0; i < MAX7219_SEG_NUM; i++) {
    max7219_word(0x0a, 0x08, i+1);  // TODO: Check if it's i+1 or MAX7219_SEG_NUM
  }
}


void Max7219::set_pixelbuffer(PixelBuffer& pixbuf) {
  this->pixelbuffer = &pixbuf;
}


PixelBuffer* Max7219::get_pixelbuffer(void) {
  return this->pixelbuffer;
}


void Max7219::show(void) {
  uint8_t bit_mask = 0x80;

  for (uint8_t row = 1; row <= 8; row++) {
    // Loop through the buffer rows
    uint8_t buffer_seg = pixelBufferLength; // Starting from the last segment of the buffer - the "-8" below.
    
    digitalWrite(MAX7219_CS, LOW);  // Set CS to LOW \___ start of transmission

    while (buffer_seg != 0) {
      // Loop through the segments
      max7219_byte(row);  // Send the address out.
      // Send 8 bits - 1 from each column
      for (uint8_t index = 8; index != 0; index--) {  // Loop through the columns
        uint8_t col = this->pixelbuffer->get_buffer_array()[buffer_seg + index - 1 - 8];
        // Send 1 bit out.
        digitalWrite(MAX7219_CLK, LOW);  // Set CLK to LOW

        if (col & bit_mask) // Mask the bit of the data
          digitalWrite(MAX7219_DIN, HIGH);  // Set DIN to HIGH
        else
          digitalWrite(MAX7219_DIN, LOW);  // Set DIN to LOW
        
        digitalWrite(MAX7219_CLK, HIGH);  // Set CLK to HIGH
      }
      buffer_seg -= 8;  // Decrease the buffer segment index.
    }
    digitalWrite(MAX7219_CS, HIGH);  // Set CS to HIGH \__ end of transmission
    digitalWrite(MAX7219_CLK, LOW);  // Set CLK to LOW
    bit_mask >>= 1;  // Shift bit mask to the left.
  }
}


#ifdef DEBUG
  PixelBuffer pb = PixelBuffer();
  Max7219 test_max72(pb);
#endif

#ifdef DEBUG
  void test_counting_int(void) {
    int num = -25;
    while (num < 25) {
      pb.set_int(num);
      pb.draw_pixelbuffer();
      test_max72.show();
      delay(200);
      num += 1;
    }
  }
#endif

#ifdef DEBUG
  void test_counting_float(void) {
    float num = -15.0;
    while (num < 15.0) {
      pb.set_float(num);
      pb.draw_pixelbuffer();
      test_max72.show();
      delay(200);
      num += 0.01;
    }
  }
#endif

#ifdef DEBUG
  void test_max_setup(void) {
    test_max72.init();

    const byte SiegHeil[] = {0, 0, 0, 0, 78, 219, 219, 114, 
                            0, 195, 255, 195, 0, 255, 219, 219, 
                            195, 0, 126, 195, 219, 123, 0, 0, 
                            0, 255, 24, 24, 255, 0, 255, 219, 
                            219, 195, 0, 195, 255, 195, 0, 255, 
                            192, 192, 192, 0, 223, 223, 0, 0, 
                            0, 0};
    for (uint8_t i=0; i<sizeof(SiegHeil); i++) {
      pb.move_pixelbuffer_left(SiegHeil[i]);
      test_max72.show();
      delay(25);
    }
    for (uint8_t i=0; i<sizeof(SiegHeil)-4; i++) {
      pb.move_pixelbuffer_left(0x00);
      test_max72.show();
      delay(25);
    }

    test_counting_int();
    test_counting_float();
  }
#endif

#ifdef DEBUG
  void test_max_loop(void) {
    const byte Heil[] = {0, 0, 0, 0, 0, 0, 0, 255, 
                        24, 24, 255, 0, 255, 219, 219, 195, 
                        0, 195, 255, 195, 0, 255, 192, 192, 
                        192, 0, 0, 0, 0, 0, 0, 0,};

    const byte Hitler[] = {0, 0, 255, 24, 24, 255, 0, 195, 
                          255, 195, 0, 7, 195, 255, 195, 7, 
                          0, 255, 192, 192, 224, 0, 255, 219, 
                          219, 195, 0, 255, 27, 59, 238, 0};
                          
    Serial.println("MAX Loop");
    pb.set_pixelbuffer(Heil, sizeof(Heil));
    test_max72.show();
    delay(500);

    pb.clear_pixelbuffer();
    test_max72.show();
    delay(600);

    pb.set_pixelbuffer(Hitler, sizeof(Hitler));
    test_max72.show();
    delay(1000);

    pb.clear_pixelbuffer();
    test_max72.show();
    delay(600);
    
    pb.set_text("1488");
    pb.draw_pixelbuffer();
    pb.center_pixelbuffer();
    test_max72.show();

    delay(750);
    pb.invert_pixelbuffer();
    test_max72.show();
    delay(750);
  }
#endif
